/**** Geometry Shader Marching Cubes
	* Copyright Cyril Crassin, January 2007.
	* This code is partially based on the example of 
	* Paul Bourke "Polygonising a scalar field" located at :
	* http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
****/

#include "GLEngineApp.h"
#include <time.h>
#include <math.h>
#include <string>
#include <iostream>
#include <fstream>

//Alex Stuff
#include "RenderComponent.h"
#include "VertexColorCube.h"
#include "Model.h"
#include "GLSLShader.h"
#include "ColorShader.h"
#include "BrickShader.h"

GLEngineApp::GLEngineApp(const char *title, int w, int h, int flags):
						AbstractClass(title, w, h, flags),m_pWeapon(0) {
	
}
GLEngineApp::GLEngineApp(int *argcp, char **argv, const char *title, int w, int h, int flags):
						AbstractClass(argcp, argv, title, w, h, flags),m_pWeapon(0) {
}

void GLEngineApp::Keyboard(unsigned char key) 
{
	if(key == 'z')
        zoom -= 2.0f;
    if(key == 's')
        zoom += 2.0f;
	if(key == 'q')
        tx += 0.5f;
	if(key == 'd')
        tx -= 0.5f;
}
void GLEngineApp::Reshape(int w, int h)
{
	//Reshaping the window
	if (h==0)										// Prevent A Divide By Zero By
	{
		h=1;										// Making Height Equal One
	}

	glViewport(0,0,w,h);						// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)w/(GLfloat)h,0.1f,1000.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();
}

void GLEngineApp::Mouse(int button, int state, int x, int y){
	lastx=x;
	lasty=y;

	switch(button)
	{
	case GLUT_LEFT_BUTTON:
		Buttons[0] = ((GLUT_DOWN==state)?1:0);
		break;
	case GLUT_MIDDLE_BUTTON:
		Buttons[1] = ((GLUT_DOWN==state)?1:0);
		break;
	case GLUT_RIGHT_BUTTON:
		Buttons[2] = ((GLUT_DOWN==state)?1:0);
		break;
	default:
		break;
	}
}


void GLEngineApp::MouseMove(int x, int y)
{

	int diffx=x-lastx;
	int diffy=y-lasty;
	lastx=x;
	lasty=y;

	if( Buttons[0] && Buttons[1] )
	{
		zoom -= (float) 0.05f * diffx;
	}
	else
		if( Buttons[0] )
		{
			rotx += (float) 0.5f * diffy;
			roty += (float) 0.5f * diffx;
		}
		else
			if( Buttons[1] )
			{
				tx += (float) 0.05f * diffx;
				ty -= (float) 0.05f * diffy;
			}
}

void GLEngineApp::MousePassiveMove(int x, int y)
{

}

void GLEngineApp::Idle() 
{ 
	//When your app is on idle
	//You could redesplay
	//this->Redisplay();
}
void GLEngineApp::Update(int value)
{
	m_Rotation += 1.0f;
	//m_pQuadTree->Tick();
}

//This is a test for the obj loader
/*GLMmodel* pmodel1 = NULL;
void drawmodel_box(void)
{
	// Load the model only if it hasn't been loaded before
	// If it's been loaded then pmodel1 should be a pointer to the model geometry data...otherwise it's null
    if (!pmodel1) 
	{
		// this is the call that actualy reads the OBJ and creates the model object
        pmodel1 = glmReadOBJ("Models/mp44.obj");	
        if (!pmodel1) exit(0);
		// This will rescale the object to fit into the unity matrix
		// Depending on your project you might want to keep the original size and positions you had in 3DS Max or GMAX so you may have to comment this.
        glmUnitize(pmodel1);
		// These 2 functions calculate triangle and vertex normals from the geometry data.
		// To be honest I had some problem with very complex models that didn't look to good because of how vertex normals were calculated
		// So if you can export these directly from you modeling tool do it and comment these line
		// 3DS Max can calculate these for you and GLM is perfectly capable of loading them
        //glmFacetNormals(pmodel1);        
		//glmVertexNormals(pmodel1, 90.0);
    }
    // This is the call that will actualy draw the model
	// Don't forget to tell it if you want textures or not :))
    glmDraw(pmodel1, GLM_SMOOTH);
	
}*/

void GLEngineApp::Display() 
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear Screen And Depth Buffer
	glLoadIdentity();									// Reset The Current Modelview Matrix
	
	glTranslatef(0,0,-zoom);
	glTranslatef(tx,ty,0);
	glRotatef(rotx,1,0,0);
	glRotatef(roty,0,1,0);

	//Model import .obj test
	//drawmodel_box();

	//This is a rendercomponent test;
	//VertexColorCube* m_Cube1 = new VertexColorCube(5,6,8);
   // m_RenderComponents.push_back(m_Cube1);

	//Draw Grid
	m_pColorShader->Activate();
	m_pColorShader->SetTransforms();

	DrawGridVA();

	for( std::vector<RenderComponent*>::iterator it = m_RenderComponents.begin(); it != m_RenderComponents.end(); ++it)
    {
        (*it)->Draw();
    }

	//m_pTerrain->RenderMarchCube();
	//m_pQuadTree->Draw();
	//glPushMatrix();
	//glTranslatef(-0.0f,-0.0f,-6.0f);						// Move Left 1.5 Units And Into The Screen 6.0
	//glRotatef(m_Rotation,0.0f,0.5f,0.5f);	
	//glBindTexture(GL_TEXTURE_2D, m_Texture[0]);
	//glBegin(GL_QUADS);
	//	// Front Face
	//	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
	//	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
	//	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
	//	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
	//	// Back Face
	//	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	//	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
	//	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
	//	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
	//	// Top Face
	//	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
	//	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
	//	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
	//	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
	//	// Bottom Face
	//	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	//	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
	//	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
	//	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
	//	// Right face
	//	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
	//	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
	//	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
	//	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
	//	// Left Face
	//	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
	//	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
	//	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
	//	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
	//glEnd();
	//glPopMatrix();
}
const GLfloat light_ambient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };
const GLfloat light_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat light_position[] = { 2.0f, 5.0f, 5.0f, 0.0f };

const GLfloat mat_ambient[]    = { 0.7f, 0.7f, 0.7f, 1.0f };
const GLfloat mat_diffuse[]    = { 0.8f, 0.8f, 0.8f, 1.0f };
const GLfloat mat_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
const GLfloat high_shininess[] = { 100.0f };
void GLEngineApp::Init()
{
	glewInit();
	offset = 0.0f;
    zoom = 1.0f;
    rotx = 0;
    roty = 0.001f;
    tx = 0;
    ty = 0;
    lastx=0;
    lasty=0;
    Buttons[0] = 0;
    Buttons[1] = 0;
    Buttons[2] = 0;

	//m_pTerrain = new Terrain(Vector3f(0,0,-1));
	//m_pQuadTree = new QuadTree();
	//m_pQuadTree->Create(m_pTerrain);
	//m_Rotation = 0;
	//m_Texture[0] = LoadBMP("Model/weapon_color.tga");

	//GLfloat LightAmbient[]=		{ 0.5f, 0.5f, 0.5f, 1.0f };
	//GLfloat LightDiffuse[]=		{ 1.0f, 1.0f, 1.0f, 1.0f };
	//GLfloat LightPosition[]=	{ 0.0f, 0.0f, 2.0f, 1.0f };

	//glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);		// Setup The Ambient Light
	//glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);		// Setup The Diffuse Light
	//glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);	// Position The Light
	//glEnable(GL_LIGHT1);

	///////////////////
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
	glClearDepth(1.0f);
    glDisable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);

    //glEnable(GL_TEXTURE_2D);
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading

    //glEnable(GL_LIGHT0);
    //glEnable(GL_NORMALIZE);
    //glEnable(GL_COLOR_MATERIAL);
    //glEnable(GL_LIGHTING);

    //glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
    //glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
    //glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    //glLightfv(GL_LIGHT0, GL_POSITION, light_position);

    //glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
    //glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
    //glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
    //glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

	//Testing ObjModel
	//m_pMyModel = new ObjModel();
	//m_pMyModel->LoadModel("Teapot.obj");
	//m_RenderComponents.push_back(m_pMyModel);

	//Init Shaders
	m_pBrickShader = new BrickShader();
	m_pColorShader = new ColorShader();

	CreateGridVA(20,0.2f);

	m_pBrickShader->Init();
	m_pColorShader->Init();

	//Initialise weapon
	m_pWeapon = new Model();
	m_pWeapon->Load("Models/mp44.obj");
	m_RenderComponents.push_back(m_pWeapon);


}
GLuint GLEngineApp::LoadBMP(char *Filename)				// Loads A Bitmap Image
{
	FILE *File=NULL;									// File Handle

	if (!Filename)										// Make Sure A Filename Was Given
	{
		return NULL;									// If Not Return NULL
	}
	GLuint tex_2d = SOIL_load_OGL_texture
	(
		Filename,
		SOIL_LOAD_AUTO,
		SOIL_CREATE_NEW_ID,
		SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT
	);
	return tex_2d; // If Load Failed Return NULL
}


// Static cheaty quick grid functions

void GLEngineApp::DrawGridVA()
{
	GLint vertexAttribLoc = glGetAttribLocation(m_pColorShader->GetProgramID(),"lPos");
	GLint colorAttribLoc = glGetAttribLocation(m_pColorShader->GetProgramID(),"color");
	//glEnableClientState(GL_ATTRIB
	glEnableVertexAttribArray( vertexAttribLoc );
	glEnableVertexAttribArray( colorAttribLoc );

	glVertexAttribPointer(vertexAttribLoc,3,GL_FLOAT,false,6*sizeof(float),m_pGridData);
	glVertexAttribPointer(colorAttribLoc,3,GL_FLOAT,false,6*sizeof(float),&(m_pGridData[0].r));

	glDrawArrays(GL_LINES,0,80);

	glDisableVertexAttribArray(vertexAttribLoc );
	glDisableVertexAttribArray(colorAttribLoc );
}

void GLEngineApp::CreateGridVA(int nrOfLines, float gridSize)
{
	m_pGridData = new vertexcolor[nrOfLines*4];
	int index = 0;
	float min = -gridSize * nrOfLines/2;
	float max = gridSize* nrOfLines/2;
	
	for ( int x = 0; x < nrOfLines/2; ++x)
	{
		m_pGridData[index++].setValues(x*gridSize,min,0,1.0,0,0);
		m_pGridData[index++].setValues(x*gridSize,max,0,1.0,0,0);
	}

	float r2 = 1.0f;
	float g2 = 115.0f/255.0f;
	float b2 = 115.0f/255.0f;
	
	for ( int x = 1; x < nrOfLines/2; ++x)
	{
		m_pGridData[index++].setValues(-x*gridSize,min,0,r2,g2,b2);
		m_pGridData[index++].setValues(-x*gridSize,max,0,r2,g2,b2);
	}

	
	for ( int y = 0; y < nrOfLines/2; ++y)
	{
		m_pGridData[index++].setValues(min,y*gridSize,0,0,1.0f,0);
		m_pGridData[index++].setValues(max,y*gridSize,0,0,1.0,0);	
	}

	float r4 = 200.0f/255.0f;
	float g4 = 1.0f;
	float b4 = 200.0f/255.0f;
	
	for ( int y = 1; y < nrOfLines/2; ++y)
	{
		m_pGridData[index++].setValues(min,-y*gridSize,0,r4,g4,b4);
		m_pGridData[index++].setValues(max,-y*gridSize,0,r4,g4,b4);
	}
}
